﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using NHibernate.Linq;


namespace NLite.NHPersistence
{
    [Component]
    public class NHQueryExtension : NLite.Persistence.IQueryExtension
    {
        public NLite.Persistence.IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector)
        {
            var req = query.Fetch<TOriginating, TRelated>(relatedObjectSelector);
            return new NhFetchRequestWrapper<TOriginating, TRelated>(req);
        }

        public NLite.Persistence.IFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector)
        {
            var req = query.FetchMany<TOriginating, TRelated>(relatedObjectSelector);
            return new NhFetchRequestWrapper<TOriginating, TRelated>(req);
        }

        public NLite.Persistence.IFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(NLite.Persistence.IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector)
        {
            var nhQuery = query as NhFetchRequestWrapper<TQueried, TFetch>;
            var req = EagerFetchingExtensionMethods.ThenFetch<TQueried, TFetch, TRelated>(nhQuery.Inner, relatedObjectSelector);
            return new NhFetchRequestWrapper<TQueried, TRelated>(req);
        }

        public NLite.Persistence.IFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(NLite.Persistence.IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector)
        {
            var nhQuery = query as NhFetchRequestWrapper<TQueried, TFetch>;
            var req = EagerFetchingExtensionMethods.ThenFetchMany<TQueried, TFetch, TRelated>(nhQuery.Inner, relatedObjectSelector);
            return new NhFetchRequestWrapper<TQueried, TRelated>(req);
        }

        public IEnumerable<T> ToFuture<T>(IQueryable<T> query)
        {
            return query.ToFuture<T>();
        }

        public IQueryable<T> Cacheable<T>(IQueryable<T> query)
        {
            return query.Cacheable<T>();
        }

        public IQueryable<T> CacheRegion<T>(IQueryable<T> query, string region)
        {
            return query.CacheRegion<T>(region);
        }

        public IQueryable<T> CacheMode<T>(IQueryable<T> query, Persistence.CacheMode cacheMode)
        {
            return query.CacheMode<T>((NHibernate.CacheMode)cacheMode);
        }
    }

    class NhFetchRequestWrapper<TOriginating, TRelated> : NLite.Persistence.IFetchRequest<TOriginating, TRelated>
    {
        internal INhFetchRequest<TOriginating, TRelated> Inner;
        public NhFetchRequestWrapper(NHibernate.Linq.INhFetchRequest<TOriginating, TRelated> inner)
        {
            Inner = inner;
        }

        public IEnumerator<TOriginating> GetEnumerator()
        {
            return Inner.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Inner.GetEnumerator();
        }

        public Type ElementType
        {
            get { return Inner.ElementType; }
        }

        public Expression Expression
        {
            get { return Inner.Expression; }
        }

        public IQueryProvider Provider
        {
            get { return Inner.Provider; }
        }
    }
}
